/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package wave;


import ast.*;
import base.*;
import base.TrackNode.Echo;
import base.WaveEnvironment.Mode;
import communicator.message.MsgCreateKNLink;
import communicator.message.MsgCreateKNNode;
import communicator.message.MsgWave;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import trackmsg.ActivateMsg;
import trackmsg.EchoMsg;
import trackmsg.ExpandRemoteTrack;
import trackmsg.ExpandhMsg;
import trackmsg.TrackCtrlMsg;


/**
 *
 * @author Vũ Đức Tiệp
 */
public class ASTHopExecution extends WaveExecution{
    public ASTHopExecution(WaveHeadParser parser, WaveEnvironment waveEnv){
        super(parser, waveEnv);
    }
    public void execute(ASTHop hop, String tail){
        Utilities.WPPrint("Execute Hop: " + hop.getString());
        //Bien kiem tra xem co trong che do CREATE hay ko
        boolean isCreate = waveEnv.getWaveMode() == Mode.CREATE;
        boolean isExpand = false;
        boolean isTrackMode = waveEnv.getTrackAddress() != null;
        String currentNodeName = waveEnv.getCONTENT();
        
        SimpleNode left = (SimpleNode)hop.jjtGetChild(0);
        SimpleNode right = (SimpleNode)hop.jjtGetChild(1);

        if(left instanceof ASTDirectLink){//@ direct link
            //Execute @# nhay den tat ca cac node
            if(right instanceof ASTAllNodes){ //@#ALL
                ArrayList<KNNode> allNodes = kn.getAllKNNodes();
                for(KNNode node : allNodes){
                    isExpand = sendWave(null, node.getName(), null, tail, waveEnv);
                }
            } else if(right instanceof  ASTIpAddress) { //@#node$$IpAddress
                
                List<String> desNodes = getASTNodeValue((SimpleNode)right.jjtGetChild(0));
                String desHost = getASTNodeValue((SimpleNode)right.jjtGetChild(1)).firstElement().toString();

                if(Utilities.isLocalHost(desHost)){//Neu ip la localhost
                    if(isCreate){//Neu trong che do CREATE thi tao ra node moi
                        kn.addKNNode(desNodes);
                    }

                    for(String desNodeName:desNodes) {
                        isExpand = sendWave(null, desNodeName, null, tail, waveEnv);
                    }
                } else { //neu ip o tren may khac
                    if(isCreate){
                        for(String desNodeName : desNodes){
                            MsgCreateKNNode msg = new MsgCreateKNNode(desNodeName);
                            msg.setDestinationComputer(desHost);
                            wi.getPropagationQueue().incoming(msg);
                        }
                    }                    
                    for(String desNodeName:desNodes) {
                        isExpand = sendWaveOut(null, desNodeName, null, tail, waveEnv, desHost);
                    }                    
                }
            } else { //@#node
                List<String> desNodes = getASTNodeValue(right);
                if(isCreate) kn.addKNNode(desNodes);
                for(String desNodeName : desNodes){
                    if(kn.getKNNode(desNodeName) != null)
                        isExpand = sendWave(null, desNodeName, null, tail, waveEnv);
                }
            }

        } else {
            if(currentNodeName == null) {
                Utilities.WPPrint("ERROR: Wave not in any KN Node!");
                return;
            }
            KNNode currentNode = kn.getKNNode(currentNodeName);
            if(currentNode == null){
                Utilities.WPPrint("ERROR: Current node " + currentNodeName +" doesn't exists!");
                return;
            }

            if(left instanceof ASTAnyLink){//ANY#...
                ArrayList<KNLink> allLinks = (ArrayList<KNLink>) currentNode.getAllLinks().clone();
                if(right instanceof ASTAllNodes){//ANY#ALL
                    for(KNLink link : allLinks){
                        if(link.isLinkedWithOtherComputer())
                            isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
                        else
                            isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
                    }                    
                } else {//ANY#node
                    System.out.println("START: ");
                    Vector<String> vRight = getASTNodeValue(right);
                    ArrayList desNodeSet = kn.getAllNeighborOf(currentNodeName);
                    for(String desNode : vRight){
                        if(desNodeSet.contains(desNode)){
                            //Xu ly nhu truoc
                            for(KNLink link : allLinks){
                                String desNodeName = link.getDesNodeName();
                                if(desNode.equals(desNodeName)){
                                    if(link.isLinkedWithOtherComputer())
                                        isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
                                    else
                                        isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
                                }
                            }
                        } else {
                            //Xu ly trong truong hop cua track                               
                            isExpand = sendWave(currentNodeName, desNode, "", "FALSE!", waveEnv);
                        }
                    }
                    System.out.println("END: ");
//                    for(KNLink link : allLinks){
//                        String desNode = link.getDesNodeName();
//                        if(vRight.contains(desNode)){
//                            if(link.isLinkedWithOtherComputer())
//                                isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
//                            else
//                                isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
//                        }
//                    }
                }
            } else {//link#...
                ArrayList<KNLink> allLinks = (ArrayList<KNLink>) currentNode.getAllLinks().clone();
                Vector<String> vLeft = new Vector();
                for(Object obj : getASTNodeValue((SimpleNode)left)){
                    vLeft.add(obj.toString());
                }
                if(right instanceof ASTAllNodes){ //link#ALL
                    //System.out.println("DEBUG: " + vLeft);
                    allLinks = (ArrayList<KNLink>) currentNode.getAllLinks().clone();
                    for (String linkName : vLeft){
                        boolean exist = false;
                        for(KNLink link: allLinks){
                            if(linkName.equals(link.getNameWithSign())){
                                exist = true;
                                if(link.isLinkedWithOtherComputer())
                                    isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
                                else
                                    isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
                            }
                        }
                        if(!exist){//Neu ko co link nao phu hop
                            isExpand = sendWave(currentNodeName, currentNodeName, linkName, "FALSE!", waveEnv);
                        }
                    }
//                    for(KNLink link : allLinks){
//                        if(vLeft.contains(link.getNameWithSign())){
//                            if(link.isLinkedWithOtherComputer())
//                                isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
//                            else
//                                isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
//                        }
//                    }
                } else if(right instanceof ASTIpAddress){ //link#node$$ip                     
                        String desNodeName = getASTNodeValue((SimpleNode)right.jjtGetChild(0)).firstElement().toString();
                        String desHost = getASTNodeValue((SimpleNode)right.jjtGetChild(1)).firstElement().toString();
                        if(Utilities.isLocalHost(desHost)){
                           if(isCreate){
                                kn.addKNNode(desNodeName);
                                for(String linkName : vLeft){
                                    kn.addKNLink(Utilities.removeSignOfLink(linkName)
                                            , Utilities.linkNameToSign(linkName), currentNode, desNodeName, desHost);
                                    kn.addKNLink(Utilities.removeSignOfLink(linkName)
                                            , -Utilities.linkNameToSign(linkName), kn.getKNNode(desNodeName), currentNodeName, desHost);
                                }
                           }
                           allLinks =(ArrayList<KNLink>) currentNode.getAllLinks().clone();
                            for(KNLink link : allLinks){
                                if(vLeft.contains(link.getNameWithSign()) && desNodeName.equals(link.getDesNodeName()))
                                    if(link.isLinkedWithOtherComputer())
                                        isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
                                    else
                                        isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
                            }
                        } else {
                            String linkName = vLeft.firstElement();
                            if(isCreate){
                               Utilities.WPPrint("Tao node tren may " + desHost);
                               KNNode curNode = kn.getKNNode(currentNodeName);
                               kn.addKNLink(Utilities.removeSignOfLink(linkName), 
                                       Utilities.linkNameToSign(linkName), currentNode, desNodeName, desHost);
                               MsgCreateKNNode createNode = new MsgCreateKNNode(desNodeName);
                               createNode.setDestinationComputer(desHost);
                               MsgCreateKNLink createLink = new MsgCreateKNLink(currentNodeName, desNodeName,
                                       linkName, null, null);
                               createLink.setDestinationComputer(desHost);
                               wi.getPropagationQueue().incoming(createNode);
                               wi.getPropagationQueue().incoming(createLink);
                           }
                           allLinks = (ArrayList<KNLink>)currentNode.getAllLinks().clone();
                            for(KNLink link : allLinks){
                                if(vLeft.contains(link.getNameWithSign()) && desNodeName.equals(link.getDesNodeName()))
                                    if(link.isLinkedWithOtherComputer())
                                        isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
                                    else
                                        isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
                            }
                        }
                } else {//link#node
                    Vector<String> vRight = getASTNodeValue(right);                    
                    if(isCreate){
                        kn.addKNNode(vRight);
                        for(String desNodeName : vRight){
                            for(String linkName : vLeft){
                                kn.addKNLink(Utilities.removeSignOfLink(linkName),
                                        Utilities.linkNameToSign(linkName), currentNode, desNodeName, null);
                                kn.addKNLink(Utilities.removeSignOfLink(linkName),
                                        -Utilities.linkNameToSign(linkName), kn.getKNNode(desNodeName), currentNodeName, null);
                            }
                        }
                    }
                    //System.out.println("DEBUG: " + vLeft);
                    allLinks = (ArrayList<KNLink>) currentNode.getAllLinks().clone();
                    for (String linkName : vLeft){
                        for(String desName : vRight) {
                            boolean exist = false;
                            for(KNLink link: allLinks){
                                if(linkName.equals(link.getNameWithSign()) && desName.equals(link.getDesNodeName())){
                                    exist = true;
                                    if(link.isLinkedWithOtherComputer())
                                        isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
                                    else
                                        isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
                                }
                            }
                            if(!exist){//Neu ko co link nao phu hop
                                isExpand = sendWave(currentNodeName, currentNodeName, linkName, "FALSE!", waveEnv);
                            }
                        }
                    }
//                    allLinks = currentNode.getAllLinks();
//                    for(KNLink link : allLinks){
//                        Utilities.WPPrint(vLeft + " : "+ link.getNameWithSign());
//                        if(vLeft.contains(link.getNameWithSign()) && vRight.contains(link.getDesNodeName()))
//                            if(link.isLinkedWithOtherComputer())
//                                isExpand = sendWaveOut(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv, link.getDesHostAddr());
//                            else
//                                isExpand = sendWave(currentNodeName, link.getDesNodeName(), link.getName(), tail, waveEnv);
//                    }
                }
            }            
        }
        if(isTrackMode){//Kiem tra trong che do track
            TrackNode currentTrack = wi.getTrackForest().
                    getTrackNode(waveEnv.getTrackAddress());
            TrackCtrlMsg msg = new ActivateMsg(currentTrack);
            if(!isExpand) msg = new EchoMsg(currentTrack, Echo.FALSE);
            wi.getTrackQueue().incoming(msg);
        }
    }
    private boolean sendWave(String srcNode, String desNode, String link,
            String tail, WaveEnvironment waveEnv){
        WaveEnvironment newEnv = new WaveEnvironment(waveEnv);
        Utilities.WPPrint("Move from " + srcNode + " by " + link + " to " + desNode);
        newEnv.setCONTENT(desNode);
        newEnv.setPREDECESSOR(srcNode);
        newEnv.setLINK(link);
        if(waveEnv.getTrackAddress() != null){
            TrackNode currentTrack = wi.getTrackForest().
                    getTrackNode(waveEnv.getTrackAddress());
            TrackCtrlMsg msg;
            msg = new ExpandhMsg(currentTrack, tail, newEnv);
            wi.getTrackQueue().incoming(msg);
            return true;
        } else {
            Wave newWave = new Wave(tail, newEnv);
            this.waveQueue.incomingFront(newWave);
            Utilities.WPPrint(newEnv.toString());
            return false;
        }
    }
    private boolean sendWaveOut(String srcNode, String desNode, String link, String tail, WaveEnvironment waveEnv, String desComputer){
        WaveEnvironment newEnv = new WaveEnvironment(waveEnv);
        Utilities.WPPrint("Move from " + srcNode + " by " + link + " to " + desNode);
        newEnv.setCONTENT(desNode);
        newEnv.setPREDECESSOR(srcNode);
        newEnv.setLINK(link);
        if(waveEnv.getTrackAddress() != null){
            TrackNode currentTrack = wi.getTrackForest().
                    getTrackNode(waveEnv.getTrackAddress());
            TrackCtrlMsg msg;
            msg = new ExpandRemoteTrack(currentTrack, tail, newEnv, desComputer);
            wi.getTrackQueue().incoming(msg);
            return true;
        } else {
            Wave newWave = new Wave(tail, newEnv);
            MsgWave msg = new MsgWave(newWave);
            msg.setDestinationComputer(desComputer);
            wi.getPropagationQueue().incoming(msg);
            return false;
        }
    }
}
